<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="表达式求值器">
<meta name="twitter:description" content="<p>定义接口和类型；定义方法；Parse函数：text/scanner包，运算符优先级处理示例；测试函数；check方法；应用到Web服务</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gopl-expression-evaluator.html"/>
<meta property="og:title" content="来玩魔王的咚 | 表达式求值器" />
<meta property="og:description" content="<p>定义接口和类型；定义方法；Parse函数：text/scanner包，运算符优先级处理示例；测试函数；check方法；应用到Web服务</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>定义接口和类型；定义方法；Parse函数：text/scanner包，运算符优先级处理示例；测试函数；check方法；应用到Web服务</p>" />

    <meta name="tags" content="gopl" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-08-12 14:20:00+08:00">
                <script>document.write(moment('2020-08-12 14:20:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-08-12 14:20:00+08:00">
                <script>document.write(moment('2020-08-12 14:20:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            表达式求值器
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#shi-li-biao-da-shi-qiu-zhi-qi">示例：表达式求值器</a><ul>
<li><a href="#ding-yi-jie-kou-he-lei-xing">定义接口和类型</a></li>
<li><a href="#ding-yi-fang-fa">定义方法</a></li>
<li><a href="#parse-han-shu">Parse函数</a></li>
<li><a href="#ce-shi-han-shu">测试函数</a></li>
<li><a href="#check-fang-fa">check 方法</a></li>
<li><a href="#web-ying-yong">Web 应用</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="shi-li-biao-da-shi-qiu-zhi-qi"><a class="toclink" href="#shi-li-biao-da-shi-qiu-zhi-qi">示例：表达式求值器</a></h3>
<p>本篇将创建简单算术表达式的一个求值器。  </p>
<h4 id="ding-yi-jie-kou-he-lei-xing"><a class="toclink" href="#ding-yi-jie-kou-he-lei-xing">定义接口和类型</a></h4>
<p>开始，先确定要使用一个接口 Expr 来代表这种语言的任意一个表达式。<br>
暂时没有任何方法，稍后再逐个添加：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// Expr: 算术表达式</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="kd">interface</span><span class="p">{}</span><span class="w"></span>
</code></pre></div>

<p>我们的表达式语言将包括以下符号：</p>
<ul>
<li>浮点数字面量</li>
<li>二元操作符：加减乘除（+、-、*、\/）</li>
<li>一元操作符：表示正数和负数的 -x 和 +x</li>
<li>函数调用：pow(x,y)、sin(x) 和 sqrt(x)</li>
<li>变量：比如 x、pi，自己定义一个变量名称，每次可以提供不用的值</li>
</ul>
<p>还要有标准的操作符优先级，以及小括号。所有的值都是 float64 类型。  </p>
<p>下面是几个示例表达式：</p>
<div class="highlight"><pre><span></span><code>sqrt(A / pi)
pow(x, 3) + pow(y, 3)
(F - 32) * 5 / 9
</code></pre></div>

<p>下面5种具体类型代表特定类型的表达式：</p>
<ul>
<li>Var ： 代表变量引用。这个类型是可导出的，至于为什么，后面会讲明</li>
<li>literal ： 代表浮点数常量</li>
<li>unary ： 代表有一个操作数的操作符表达式，操作数可以是任意的 Expr</li>
<li>binary ： 代表有两个操作数的操作符表达式，操作数可以是任意的 Expr</li>
<li>call ： 代表函数调用，这里限制它的 fn 字段只能是 pow、sin、sqrt</li>
</ul>
<p>为了要计算包含变量的表达式，还需要一个上下文（environment）来把变量映射到数值。<br>
所有接口和类型的定义如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">eval</span><span class="w"></span>

<span class="c1">// Expr: 算术表达式</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 返回表达式在 env 上下文下的值</span><span class="w"></span>
<span class="w">    </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="w">    </span><span class="c1">// Check 方法报告表达式中的错误，并把表达式中的变量加入 Vars 中</span><span class="w"></span>
<span class="w">    </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// Var 表示一个变量，比如：x.</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Var</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>

<span class="c1">// Env 变量到数值的映射关系</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Env</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">float64</span><span class="w"></span>

<span class="c1">// literal 是一个数字常量，比如：3.1415926</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">literal</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>

<span class="c1">// unary 表示一元操作符表达式，比如：-x</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">unary</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">op</span><span class="w"> </span><span class="kt">rune</span><span class="w"> </span><span class="c1">// one of &#39;+&#39;, &#39;-&#39;</span><span class="w"></span>
<span class="w">    </span><span class="nx">x</span><span class="w">  </span><span class="nx">Expr</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// binary 表示二元操作符表达式，比如：x+y.</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">binary</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">op</span><span class="w">   </span><span class="kt">rune</span><span class="w"> </span><span class="c1">// one of &#39;+&#39;, &#39;-&#39;, &#39;*&#39;, &#39;/&#39;</span><span class="w"></span>
<span class="w">    </span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="w"> </span><span class="nx">Expr</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// call 表示函数调用表达式，比如：sin(x).</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">call</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fn</span><span class="w">   </span><span class="kt">string</span><span class="w"> </span><span class="c1">// one of &quot;pow&quot;, &quot;sin&quot;, &quot;sqrt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="nx">args</span><span class="w"> </span><span class="p">[]</span><span class="nx">Expr</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>在定义好各种类型后，发现每个类型都需要提供一个 Eval 方法，于是加把这个方法加到接口中，已经添加到上面的代码中了。<br>
这个包只导出了 Expr、Var、Env。客户端可以在不接触其他表达式类型的情况下使用这个求值器。  </p>
<h4 id="ding-yi-fang-fa"><a class="toclink" href="#ding-yi-fang-fa">定义方法</a></h4>
<p>接下来实现每个类型的 Eval 方法来满足接口：</p>
<ul>
<li>Var 的 Eval 方法从上下文中查询结果，如果变量不存在，则会返回0。  </li>
<li>literal 的 Eval 方法直接返回本身的值。  </li>
<li>unbary 的 Eval 方法首先对操作数递归求值，然后应用 op 操作符。  </li>
<li>binary 的 Eval 方法的处理逻辑和 unbary 一样。  </li>
<li>call 的 Eval 方法先对 pow、sin、sqrt 函数的参数求值，再调用 math 包中的对应函数。  </li>
</ul>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">eval</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;math&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="nx">Var</span><span class="p">)</span><span class="w"> </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">env</span><span class="p">[</span><span class="nx">v</span><span class="p">]</span><span class="w"> </span><span class="c1">// 如果查询不到变量名，则返回类型的零值，就是0</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">l</span><span class="w"> </span><span class="nx">literal</span><span class="p">)</span><span class="w"> </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nb">float64</span><span class="p">(</span><span class="nx">l</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">u</span><span class="w"> </span><span class="nx">unary</span><span class="p">)</span><span class="w"> </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">op</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="o">+</span><span class="nx">u</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;-&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="o">-</span><span class="nx">u</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;unsupported unary operator: %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">op</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">b</span><span class="w"> </span><span class="nx">binary</span><span class="p">)</span><span class="w"> </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">op</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;-&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;*&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;/&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;unsupported binary operator: %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">op</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">call</span><span class="p">)</span><span class="w"> </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nx">fn</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;pow&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Pow</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">),</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;sin&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Sin</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="s">&quot;sqrt&quot;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Sqrt</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;unsupported function call: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nx">fn</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>某些方法可能会失败，有些错误会导致 Eval 崩溃，还有些会导致返回不正确的结果。所有这些错误可以在求值之前做检查来发现，所以还需要一个Check方法。不过暂时可以先不管Check方法，而是把 Eval 方法用起来，并通过测试进行验证。  </p>
<h4 id="parse-han-shu"><a class="toclink" href="#parse-han-shu">Parse函数</a></h4>
<p>要验证 Eval 方法，首先需要得到对象，然后调用对像的 Eval 方法。而对象需要通过解析字符串来获取，这就需要一个 Parse 函数。  </p>
<h5 id="textscanner-bao-de-shi-yong"><a class="toclink" href="#textscanner-bao-de-shi-yong">text/scanner 包的使用</a></h5>
<p>词法分析器 lexer 使用 text/scanner 包提供的扫描器 Scanner 类型来把输入流分解成一系列的标记（token），包括注释、标识符、字符串字面量和数字字面量。扫描器的 Scan 方法将提前扫描并返回下一个标记（类型为 rune）。大部分标记（比如'('）都只包含单个rune，但 text/scanner 包也可以支持由多个字符组成的记号。调用 Scan 会返回标记的类型，调用 TokenText 则会返回标记的文本。<br>
因为每个解析器可能需要多次使用当前的记号，但是 Scan 会一直向前扫描，所以把扫描器封装到一个 lexer 辅助类型中，其中保存了 Scan 最近返回的标记。<br>
下面是一个简单的用法示例：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;os&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;text/scanner&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">lexer</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">scan</span><span class="w">  </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Scanner</span><span class="w"></span>
<span class="w">    </span><span class="nx">token</span><span class="w"> </span><span class="kt">rune</span><span class="w"> </span><span class="c1">// 当前标记</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">next</span><span class="p">()</span><span class="w">        </span><span class="p">{</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Scan</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">text</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">TokenText</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="c1">// consume 方法并没有被使用到，包括后面的Pause函数</span><span class="w"></span>
<span class="c1">// 不过这是一个可复用的处理逻辑</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">consume</span><span class="p">(</span><span class="nx">want</span><span class="w"> </span><span class="kt">rune</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">want</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 注意: 错误处理不是这篇的重点，简单粗暴的处理了</span><span class="w"></span>
<span class="w">        </span><span class="nb">panic</span><span class="p">(</span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;got %q, want %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">(),</span><span class="w"> </span><span class="nx">want</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">input</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">lexer</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Init</span><span class="p">(</span><span class="nx">strings</span><span class="p">.</span><span class="nx">NewReader</span><span class="p">(</span><span class="nx">input</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Mode</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanIdents</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanInts</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanFloats</span><span class="w"></span>

<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">input</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;:&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">EOF</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;\t&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">TokenString</span><span class="p">(</span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="p">),</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span><span class="w"></span>
<span class="w">            </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>执行效果如下：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\localdemo\parse&gt; go run main.go &quot;sqrt(A / pi)&quot; &quot;pow(x, 3) + pow(y, 3)&quot; &quot;(F - 32) * 5 / 9&quot;
sqrt(A / pi) :
         Ident sqrt
         &quot;(&quot; (
         Ident A
         &quot;/&quot; /
         Ident pi
         &quot;)&quot; )
pow(x, 3) + pow(y, 3) :
         Ident pow
         &quot;(&quot; (
         Ident x
         &quot;,&quot; ,
         Int 3
         &quot;)&quot; )
         &quot;+&quot; +
         Ident pow
         &quot;(&quot; (
         Ident y
         &quot;,&quot; ,
         Int 3
         &quot;)&quot; )
(F - 32) * 5 / 9 :
         &quot;(&quot; (
         Ident F
         &quot;-&quot; -
         Int 32
         &quot;)&quot; )
         &quot;*&quot; *
         Int 5
         &quot;/&quot; /
         Int 9
PS G:\Steed\Documents\Go\src\localdemo\parse&gt;
</code></pre></div>

<h5 id="parse-han-shu_1"><a class="toclink" href="#parse-han-shu_1">Parse 函数</a></h5>
<p>Parse 函数，递归地将字符串解析为表达式。<br>
下面是完整的代码：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">eval</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strconv&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;text/scanner&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">lexer</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">scan</span><span class="w">  </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Scanner</span><span class="w"></span>
<span class="w">    </span><span class="nx">token</span><span class="w"> </span><span class="kt">rune</span><span class="w"> </span><span class="c1">// 当前标记</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">next</span><span class="p">()</span><span class="w">        </span><span class="p">{</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Scan</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">text</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">TokenText</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="kd">type</span><span class="w"> </span><span class="nx">lexPanic</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>

<span class="c1">// describe 返回一个描述当前标记的字符串，用于错误处理</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">describe</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">EOF</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;end of file&quot;</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Ident</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;identifier %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Float</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;number %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nb">rune</span><span class="p">(</span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="p">))</span><span class="w"> </span><span class="c1">// any other rune</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">precedence</span><span class="p">(</span><span class="nx">op</span><span class="w"> </span><span class="kt">rune</span><span class="p">)</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">op</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;*&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;/&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="mi">2</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="p">,</span><span class="w"> </span><span class="sc">&#39;-&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="mi">1</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// Parse 将字符串解析为表达式</span><span class="w"></span>
<span class="c1">//</span><span class="w"></span>
<span class="c1">//   expr = num                         a literal number, e.g., 3.14159</span><span class="w"></span>
<span class="c1">//        | id                          a variable name, e.g., x</span><span class="w"></span>
<span class="c1">//        | id &#39;(&#39; expr &#39;,&#39; ... &#39;)&#39;     a function call</span><span class="w"></span>
<span class="c1">//        | &#39;-&#39; expr                    a unary operator (+-)</span><span class="w"></span>
<span class="c1">//        | expr &#39;+&#39; expr               a binary operator (+-*/)</span><span class="w"></span>
<span class="c1">//</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">Parse</span><span class="p">(</span><span class="nx">input</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">_</span><span class="w"> </span><span class="nx">Expr</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="c1">// 选择性地使用 recover</span><span class="w"></span>
<span class="w">        </span><span class="c1">// 已经将 panic value 设置成特殊类型 lexPanic</span><span class="w"></span>
<span class="w">        </span><span class="c1">// 在 recover 时对 panic value 进行检查</span><span class="w"></span>
<span class="w">        </span><span class="k">switch</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">recover</span><span class="p">().(</span><span class="kd">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="kc">nil</span><span class="p">:</span><span class="w"></span>
<span class="w">            </span><span class="c1">// no panic</span><span class="w"></span>
<span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="nx">lexPanic</span><span class="p">:</span><span class="w"></span>
<span class="w">            </span><span class="c1">// 如果发现 panic value 是特殊类型，就将这个 panic 作为 errror 处理</span><span class="w"></span>
<span class="w">            </span><span class="nx">err</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">x</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">default</span><span class="p">:</span><span class="w"></span>
<span class="w">            </span><span class="c1">// 如果不是，则按照正常的 panic 进行处理</span><span class="w"></span>
<span class="w">            </span><span class="nb">panic</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="nx">lex</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">lexer</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Init</span><span class="p">(</span><span class="nx">strings</span><span class="p">.</span><span class="nx">NewReader</span><span class="p">(</span><span class="nx">input</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="nx">lex</span><span class="p">.</span><span class="nx">scan</span><span class="p">.</span><span class="nx">Mode</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanIdents</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanInts</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">ScanFloats</span><span class="w"></span>
<span class="w">    </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// 获取第一个标记</span><span class="w"></span>
<span class="w">    </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">parseExpr</span><span class="p">(</span><span class="nx">lex</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">EOF</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;unexpected %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">describe</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">parseExpr</span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">parseBinary</span><span class="p">(</span><span class="nx">lex</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="p">}</span><span class="w"></span>

<span class="c1">// binary = unary (&#39;+&#39; binary)*</span><span class="w"></span>
<span class="c1">// parseBinary 遇到优先级低于 prec1 的运算符时就停止</span><span class="w"></span>
<span class="c1">// 这个递归处理计算优先级的循环策略比较难理解</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">parseBinary</span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">,</span><span class="w"> </span><span class="nx">prec1</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">lhs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">parseUnary</span><span class="p">(</span><span class="nx">lex</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">prec</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">precedence</span><span class="p">(</span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="p">);</span><span class="w"> </span><span class="nx">prec</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="nx">prec1</span><span class="p">;</span><span class="w"> </span><span class="nx">prec</span><span class="o">--</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">precedence</span><span class="p">(</span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nx">prec</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">op</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"></span>
<span class="w">            </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume operator</span><span class="w"></span>
<span class="w">            </span><span class="nx">rhs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">parseBinary</span><span class="p">(</span><span class="nx">lex</span><span class="p">,</span><span class="w"> </span><span class="nx">prec</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="c1">// 优先级加1，进入下一次递归</span><span class="w"></span>
<span class="w">            </span><span class="nx">lhs</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">binary</span><span class="p">{</span><span class="nx">op</span><span class="p">,</span><span class="w"> </span><span class="nx">lhs</span><span class="p">,</span><span class="w"> </span><span class="nx">rhs</span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">lhs</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// unary = &#39;+&#39; expr | primary</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">parseUnary</span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;+&#39;</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="sc">&#39;-&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">op</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;+&#39; or &#39;-&#39;</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">unary</span><span class="p">{</span><span class="nx">op</span><span class="p">,</span><span class="w"> </span><span class="nx">parseUnary</span><span class="p">(</span><span class="nx">lex</span><span class="p">)}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">parsePrimary</span><span class="p">(</span><span class="nx">lex</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// primary = id</span><span class="w"></span>
<span class="c1">//         | id &#39;(&#39; expr &#39;,&#39; ... &#39;,&#39; expr &#39;)&#39;</span><span class="w"></span>
<span class="c1">//         | num</span><span class="w"></span>
<span class="c1">//         | &#39;(&#39; expr &#39;)&#39;</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">parsePrimary</span><span class="p">(</span><span class="nx">lex</span><span class="w"> </span><span class="o">*</span><span class="nx">lexer</span><span class="p">)</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Ident</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">id</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume Ident</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;(&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="nx">Var</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;(&#39;</span><span class="w"></span>
<span class="w">        </span><span class="kd">var</span><span class="w"> </span><span class="nx">args</span><span class="w"> </span><span class="p">[]</span><span class="nx">Expr</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;)&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="nx">args</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">append</span><span class="p">(</span><span class="nx">args</span><span class="p">,</span><span class="w"> </span><span class="nx">parseExpr</span><span class="p">(</span><span class="nx">lex</span><span class="p">))</span><span class="w"></span>
<span class="w">                </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;,&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                    </span><span class="k">break</span><span class="w"></span>
<span class="w">                </span><span class="p">}</span><span class="w"></span>
<span class="w">                </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;,&#39;</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">            </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;)&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">                </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;got %q, want &#39;)&#39;&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="p">)</span><span class="w"></span>
<span class="w">                </span><span class="nb">panic</span><span class="p">(</span><span class="nx">lexPanic</span><span class="p">(</span><span class="nx">msg</span><span class="p">))</span><span class="w"></span>
<span class="w">            </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;)&#39;</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">call</span><span class="p">{</span><span class="nx">id</span><span class="p">,</span><span class="w"> </span><span class="nx">args</span><span class="p">}</span><span class="w"></span>

<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Int</span><span class="p">,</span><span class="w"> </span><span class="nx">scanner</span><span class="p">.</span><span class="nx">Float</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">strconv</span><span class="p">.</span><span class="nx">ParseFloat</span><span class="p">(</span><span class="nx">lex</span><span class="p">.</span><span class="nx">text</span><span class="p">(),</span><span class="w"> </span><span class="mi">64</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nb">panic</span><span class="p">(</span><span class="nx">lexPanic</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nx">Error</span><span class="p">()))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume number</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">literal</span><span class="p">(</span><span class="nx">f</span><span class="p">)</span><span class="w"></span>

<span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="sc">&#39;(&#39;</span><span class="p">:</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;(&#39;</span><span class="w"></span>
<span class="w">        </span><span class="nx">e</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">parseExpr</span><span class="p">(</span><span class="nx">lex</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">token</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="sc">&#39;)&#39;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;got %s, want &#39;)&#39;&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">describe</span><span class="p">())</span><span class="w"></span>
<span class="w">            </span><span class="nb">panic</span><span class="p">(</span><span class="nx">lexPanic</span><span class="p">(</span><span class="nx">msg</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">lex</span><span class="p">.</span><span class="nx">next</span><span class="p">()</span><span class="w"> </span><span class="c1">// consume &#39;)&#39;</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">e</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">msg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;unexpected %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">lex</span><span class="p">.</span><span class="nx">describe</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="nb">panic</span><span class="p">(</span><span class="nx">lexPanic</span><span class="p">(</span><span class="nx">msg</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>整体的逻辑都比较难理解。parseBinary 函数是负责解析二元表达式的，其中包括了对运算符优先级的处理（<em>逻辑比较难懂，自己想不出来，看也没完全看懂，以后有类似的实现或许可以借鉴</em>）。  </p>
<h4 id="ce-shi-han-shu"><a class="toclink" href="#ce-shi-han-shu">测试函数</a></h4>
<p>下面的 TestEval 函数用于测试求值器，它使用 testing 包，使用<strong>基于表</strong>的测试方式。表格中定义了三个表达式并为每个表达式准备了不同的上下文。<br>
第一个表达式用于根据圆面积A求半径，第二个用于计算两个变量x和y的立方和，第三个把华氏温度F转为摄氏温度：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">eval</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;math&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;testing&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">TestEval</span><span class="p">(</span><span class="nx">t</span><span class="w"> </span><span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">tests</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="p">[]</span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">expr</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">        </span><span class="nx">env</span><span class="w">  </span><span class="nx">Env</span><span class="w"></span>
<span class="w">        </span><span class="nx">want</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="p">}{</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;sqrt(A / pi)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;A&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">87616</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;pi&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Pi</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;167&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;pow(x, 3) + pow(y, 3)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;1729&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;pow(x, 3) + pow(y, 3)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;1729&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;5 / 9 * (F - 32)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;F&quot;</span><span class="p">:</span><span class="w"> </span><span class="o">-</span><span class="mi">40</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;-40&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;5 / 9 * (F - 32)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;F&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">32</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;0&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">        </span><span class="p">{</span><span class="s">&quot;5 / 9 * (F - 32)&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;F&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">212</span><span class="p">},</span><span class="w"> </span><span class="s">&quot;100&quot;</span><span class="p">},</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">prevExpr</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">tests</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="c1">// 仅在表达式变更时才输出</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">expr</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">prevExpr</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;\n%s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">expr</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">prevExpr</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">expr</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">expr</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">Parse</span><span class="p">(</span><span class="nx">test</span><span class="p">.</span><span class="nx">expr</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">t</span><span class="p">.</span><span class="nx">Error</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"> </span><span class="c1">// 解析出错</span><span class="w"></span>
<span class="w">            </span><span class="k">continue</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">        </span><span class="nx">got</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%.6g&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">expr</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">test</span><span class="p">.</span><span class="nx">env</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;\t%v =&gt; %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">env</span><span class="p">,</span><span class="w"> </span><span class="nx">got</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">got</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">want</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">t</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;%s.Eval() in %v = %q, want %q\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">expr</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">env</span><span class="p">,</span><span class="w"> </span><span class="nx">got</span><span class="p">,</span><span class="w"> </span><span class="nx">test</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>对于表格中的每一行记录，先解析表达式，在上下文中求值，再输出表达式。<br>
启用 -v 选项查看测试的输出：</p>
<div class="highlight"><pre><span></span><code>PS G:\Steed\Documents\Go\src\gopl\output\expression_evaluator\eval&gt; go test -v
=== RUN   TestEval

sqrt(A / pi)
        map[A:87616 pi:3.141592653589793] =&gt; 167

pow(x, 3) + pow(y, 3)
        map[x:12 y:1] =&gt; 1729
        map[x:9 y:10] =&gt; 1729

5 / 9 * (F - 32)
        map[F:-40] =&gt; -40
        map[F:32] =&gt; 0
        map[F:212] =&gt; 100
--- PASS: TestEval (0.00s)
PASS
ok      gopl/output/expression_evaluator/eval   0.329s
PS G:\Steed\Documents\Go\src\gopl\output\expression_evaluator\eval&gt;
</code></pre></div>

<h4 id="check-fang-fa"><a class="toclink" href="#check-fang-fa">check 方法</a></h4>
<p>到目前为止，所有的输入都是合法的，但是并不是总能如此。即使在解释性语言中，通过语法检查来发现<strong>静态</strong>错误（即不用运行程序也能检测出来的错误）也是很常见的。通过分离静态检查和动态检查，可以更快发现错误，也可以只在运行前检查一次，而不用在表达式求值时每次都检查。<br>
现在就给 Expr 加上一个 Check 方法，用于在表达式语法树上检查静态错误。这个 Check 方法有一个 vars 参数，并不是因为需要传参，而是为了让递归调用的实现起来更方便。<br>
具体看后面的代码和说明：</p>
<div class="highlight"><pre><span></span><code><span class="c1">// Expr: 算术表达式</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">Expr</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 返回表达式在 env 上下文下的值</span><span class="w"></span>
<span class="w">    </span><span class="nx">Eval</span><span class="p">(</span><span class="nx">env</span><span class="w"> </span><span class="nx">Env</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"></span>
<span class="w">    </span><span class="c1">// Check 方法报告表达式中的错误，并把表达式中的变量加入 Vars 中</span><span class="w"></span>
<span class="w">    </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>literal 和 Var 的求值不可能出错，所以直接返回 nil。unary 和 binary 的方法首先检查操作符是否合法，再递归地检查操作数。类似地，call 的方法首先检查函数是否是已知的，然后检查参数个数，最后递归地检查每个参数。<br>
具体的 Check 方法如下所示:</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">eval</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;strings&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">v</span><span class="w"> </span><span class="nx">Var</span><span class="p">)</span><span class="w"> </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">vars</span><span class="p">[</span><span class="nx">v</span><span class="p">]</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">literal</span><span class="p">)</span><span class="w"> </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">u</span><span class="w"> </span><span class="nx">unary</span><span class="p">)</span><span class="w"> </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">strings</span><span class="p">.</span><span class="nx">ContainsRune</span><span class="p">(</span><span class="s">&quot;+-&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">op</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;unexpected unary op %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">op</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">b</span><span class="w"> </span><span class="nx">binary</span><span class="p">)</span><span class="w"> </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">strings</span><span class="p">.</span><span class="nx">ContainsRune</span><span class="p">(</span><span class="s">&quot;+-*/&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">op</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;unexpected binary op %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">op</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">.</span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">b</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">c</span><span class="w"> </span><span class="nx">call</span><span class="p">)</span><span class="w"> </span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="kt">error</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">arity</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">numParams</span><span class="p">[</span><span class="nx">c</span><span class="p">.</span><span class="nx">fn</span><span class="p">]</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">!</span><span class="nx">ok</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;unknown function %q&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nx">fn</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nx">arity</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;call to %s has %d args, want %d&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">            </span><span class="nx">c</span><span class="p">.</span><span class="nx">fn</span><span class="p">,</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">),</span><span class="w"> </span><span class="nx">arity</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">arg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">arg</span><span class="p">.</span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 已知的函数名称和对应的参数个数</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">numParams</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">int</span><span class="p">{</span><span class="s">&quot;pow&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;sin&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;sqrt&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>关于递归的实现。Check 的输入参数是一个 Var 集合，这个集合是表达式中的变量名。要让表达式能成功求值，上下文必须包含所有的变量。从逻辑上来讲，这个集合应当是 Check 的输出结果而不是输入参数，但因为这个方法是递归调用的，在这种情况下使用参数更为方便。调用方最初调用时需要提供一个空的集合。  </p>
<h4 id="web-ying-yong"><a class="toclink" href="#web-ying-yong">Web 应用</a></h4>
<p>这篇里已经有一个绘制函数 z=f(x,y) 的 SVG 图形的实现了：<br>
<a href="https://steeed.gitee.io/gopl-gif-svg-png.html#_1">https://steeed.gitee.io/gopl-gif-svg-png.html#_1</a>  </p>
<p>不过当时的函数 f 是在编译的时候指定的。既然这里可以对字符串形式的表达式进行解析、检查和求值，那么就可以构建一个 Web 应用，在运行时从客户端接收一个表达式，并绘制函数的曲面图。可以使用 vars 集合来检查表达式是否是一个只有两个变量x、y的函数（为了简单起见，还提供了半径r，所以实际上是3个变量）。使用 Check 方法来拒绝掉不规范的表达式，这样就不会在下面函数的40000个计算过程中（100x100的格子，每一个有4个角）重复这些检查。<br>
表达式求值器已经完成了，把它作为一个包引入。然后把绘制函数图形加上 Web 应用的代码重新实现一遍。<br>
完整的代码如下：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;io&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;log&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;math&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;net/http&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="s">&quot;gopl/output/expression_evaluator/eval&quot;</span><span class="w"></span>

<span class="kd">const</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="nx">width</span><span class="p">,</span><span class="w"> </span><span class="nx">height</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">600</span><span class="p">,</span><span class="w"> </span><span class="mi">320</span><span class="w">            </span><span class="c1">// canvas size in pixels</span><span class="w"></span>
<span class="w">    </span><span class="nx">cells</span><span class="w">         </span><span class="p">=</span><span class="w"> </span><span class="mi">100</span><span class="w">                 </span><span class="c1">// number of grid cells</span><span class="w"></span>
<span class="w">    </span><span class="nx">xyrange</span><span class="w">       </span><span class="p">=</span><span class="w"> </span><span class="mf">30.0</span><span class="w">                </span><span class="c1">// x, y axis range (-xyrange..+xyrange)</span><span class="w"></span>
<span class="w">    </span><span class="nx">xyscale</span><span class="w">       </span><span class="p">=</span><span class="w"> </span><span class="nx">width</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nx">xyrange</span><span class="w"> </span><span class="c1">// pixels per x or y unit</span><span class="w"></span>
<span class="w">    </span><span class="nx">zscale</span><span class="w">        </span><span class="p">=</span><span class="w"> </span><span class="nx">height</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mf">0.4</span><span class="w">        </span><span class="c1">// pixels per z unit</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">sin30</span><span class="p">,</span><span class="w"> </span><span class="nx">cos30</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mf">0.5</span><span class="p">,</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Sqrt</span><span class="p">(</span><span class="mf">3.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">4.0</span><span class="p">)</span><span class="w"> </span><span class="c1">// sin(30°), cos(30°)</span><span class="w"></span>


<span class="kd">func</span><span class="w"> </span><span class="nx">corner</span><span class="p">(</span><span class="nx">f</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="w"> </span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="kt">float64</span><span class="p">,</span><span class="w"> </span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// find point (x,y) at corner of cell (i,j)</span><span class="w"></span>
<span class="w">    </span><span class="nx">x</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">xyrange</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="nb">float64</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span><span class="o">/</span><span class="nx">cells</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mf">0.5</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">y</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">xyrange</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="nb">float64</span><span class="p">(</span><span class="nx">j</span><span class="p">)</span><span class="o">/</span><span class="nx">cells</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mf">0.5</span><span class="p">)</span><span class="w"></span>

<span class="w">    </span><span class="nx">z</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">f</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="p">)</span><span class="w"> </span><span class="c1">// compute surface height z</span><span class="w"></span>

<span class="w">    </span><span class="c1">// project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy)</span><span class="w"></span>
<span class="w">    </span><span class="nx">sx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">width</span><span class="o">/</span><span class="mi">2</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="nx">x</span><span class="o">-</span><span class="nx">y</span><span class="p">)</span><span class="o">*</span><span class="nx">cos30</span><span class="o">*</span><span class="nx">xyscale</span><span class="w"></span>
<span class="w">    </span><span class="nx">sy</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">height</span><span class="o">/</span><span class="mi">2</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="nx">x</span><span class="o">+</span><span class="nx">y</span><span class="p">)</span><span class="o">*</span><span class="nx">sin30</span><span class="o">*</span><span class="nx">xyscale</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">z</span><span class="o">*</span><span class="nx">zscale</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">sx</span><span class="p">,</span><span class="w"> </span><span class="nx">sy</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">surface</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">io</span><span class="p">.</span><span class="nx">Writer</span><span class="p">,</span><span class="w"> </span><span class="nx">f</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="w"> </span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; &quot;</span><span class="o">+</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;style=&#39;stroke: grey; fill: white; stroke-width: 0.7&#39; &quot;</span><span class="o">+</span><span class="w"></span>
<span class="w">        </span><span class="s">&quot;width=&#39;%d&#39; height=&#39;%d&#39;&gt;&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">width</span><span class="p">,</span><span class="w"> </span><span class="nx">height</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">cells</span><span class="p">;</span><span class="w"> </span><span class="nx">i</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="nx">j</span><span class="w"> </span><span class="p">&lt;</span><span class="w"> </span><span class="nx">cells</span><span class="p">;</span><span class="w"> </span><span class="nx">j</span><span class="o">++</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="nx">ax</span><span class="p">,</span><span class="w"> </span><span class="nx">ay</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">corner</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">bx</span><span class="p">,</span><span class="w"> </span><span class="nx">by</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">corner</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">cx</span><span class="p">,</span><span class="w"> </span><span class="nx">cy</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">corner</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">dx</span><span class="p">,</span><span class="w"> </span><span class="nx">dy</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">corner</span><span class="p">(</span><span class="nx">f</span><span class="p">,</span><span class="w"> </span><span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nx">j</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;&lt;polygon points=&#39;%g,%g %g,%g %g,%g %g,%g&#39;/&gt;\n&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w">                </span><span class="nx">ax</span><span class="p">,</span><span class="w"> </span><span class="nx">ay</span><span class="p">,</span><span class="w"> </span><span class="nx">bx</span><span class="p">,</span><span class="w"> </span><span class="nx">by</span><span class="p">,</span><span class="w"> </span><span class="nx">cx</span><span class="p">,</span><span class="w"> </span><span class="nx">cy</span><span class="p">,</span><span class="w"> </span><span class="nx">dx</span><span class="p">,</span><span class="w"> </span><span class="nx">dy</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintln</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;&lt;/svg&gt;&quot;</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 组合了解析（Parse方法）和检查（Check方法）步骤</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">parseAndCheck</span><span class="p">(</span><span class="nx">s</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="nx">eval</span><span class="p">.</span><span class="nx">Expr</span><span class="p">,</span><span class="w"> </span><span class="kt">error</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">s</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">&quot;&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;empty expression&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">expr</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">eval</span><span class="p">.</span><span class="nx">Parse</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">vars</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="nx">eval</span><span class="p">.</span><span class="nx">Var</span><span class="p">]</span><span class="kt">bool</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">expr</span><span class="p">.</span><span class="nx">Check</span><span class="p">(</span><span class="nx">vars</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="k">range</span><span class="w"> </span><span class="nx">vars</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&quot;x&quot;</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&quot;y&quot;</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&quot;r&quot;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="kc">nil</span><span class="p">,</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Errorf</span><span class="p">(</span><span class="s">&quot;undefined variable: %s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">v</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">expr</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="c1">// 解析并检查Get请求的表达式，用它来创建一个有两个变量的匿名函数。</span><span class="w"></span>
<span class="c1">// 这个匿名函数与曲面绘制程序中的f有同样的签名。</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">plot</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">r</span><span class="p">.</span><span class="nx">ParseForm</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">expr</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">parseAndCheck</span><span class="p">(</span><span class="nx">r</span><span class="p">.</span><span class="nx">Form</span><span class="p">.</span><span class="nx">Get</span><span class="p">(</span><span class="s">&quot;expr&quot;</span><span class="p">))</span><span class="w"></span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">http</span><span class="p">.</span><span class="nx">Error</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;bad expr: &quot;</span><span class="o">+</span><span class="nx">err</span><span class="p">.</span><span class="nx">Error</span><span class="p">(),</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nx">w</span><span class="p">.</span><span class="nx">Header</span><span class="p">().</span><span class="nx">Set</span><span class="p">(</span><span class="s">&quot;Content-Type&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;image/svg+xml&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">surface</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="w"> </span><span class="kt">float64</span><span class="p">)</span><span class="w"> </span><span class="kt">float64</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">r</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">math</span><span class="p">.</span><span class="nx">Hypot</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="nx">y</span><span class="p">)</span><span class="w"> </span><span class="c1">// distance from (0,0)</span><span class="w"></span>
<span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="nx">expr</span><span class="p">.</span><span class="nx">Eval</span><span class="p">(</span><span class="nx">eval</span><span class="p">.</span><span class="nx">Env</span><span class="p">{</span><span class="s">&quot;x&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">x</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;y&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">y</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;r&quot;</span><span class="p">:</span><span class="w"> </span><span class="nx">r</span><span class="p">})</span><span class="w"></span>
<span class="w">    </span><span class="p">})</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;http://localhost:8000/plot?expr=sin(-x)*pow(1.5,-r)&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;http://localhost:8000/plot?expr=pow(2,sin(y))*pow(2,sin(x))/12&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="s">&quot;http://localhost:8000/plot?expr=sin(x*y/10)/10&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">http</span><span class="p">.</span><span class="nx">HandleFunc</span><span class="p">(</span><span class="s">&quot;/plot&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">plot</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">log</span><span class="p">.</span><span class="nx">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">ListenAndServe</span><span class="p">(</span><span class="s">&quot;localhost:8000&quot;</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">))</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>重点看 parseAndCheck 函数，组合了解析和检查的步骤。<br>
还有 plot 函数，函数的签名与 http.HandlerFunc 类似。解析并检查 HTTP 请求中的表达式，并用它来创建一个有两个变量的匿名函数。这个匿名函数与原始曲面绘制程序中的 f 有同样的签名，并且能对用户提供的表达式进行求值。上下文定义了x、y和半径r。最后，plot 调用了 surface 函数，这里略做了修改，原本直接使用函数 f，现在把函数 f 作为参数传入。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>